#include "expr.h"
#include <assert.h>
#include <stdarg.h>
#include "token.h"
#include "common.h"
#include <map>

// #define PRINT_PROCESS
// #define MARK_POS

#if defined PRINT_PROCESS && defined DEBUG
#define dbg(x) x
#else
#define dbg(...)
#endif

#if defined MARK_POS && defined DEBUG
#define mark_pos() do {\
    fprint_location(stdout, &tokc.loc);\
    printf("\n");\
} while (0)
#else
#define mark_pos(...)
#endif

#define NEW_EXPR(expr) \
    do {\
        expr = new expr_t;\
        if (expr == nullptr)\
            return nullptr;\
        expr->loc = tokc.loc;\
        expr->kind = NK_Expression;\
    } while (0)

#define EXPECT_TOKEN(expr, token) \
do {\
    if (tokc.kind != token) {\
        mark_line();\
        expect_token(token);\
        free_expression(expr);\
        expr = nullptr;\
        return nullptr;\
    }\
} while (0)

#define NEXT_ON_KIND(expr, token) \
do {\
    if (tokc.kind != token) {\
        mark_line();\
        expect_token(token);\
        free_expression(expr);\
        expr = nullptr;\
        return nullptr;\
    } else {\
        token_next();\
    }\
} while (0)

/**
 * The token's corresponding unary or binary opearator.
 * Some tokens represent both unary and binary operator,
 * e.g. * or +.
 */
 /**
	TOKENOP(TK_LSHIFT,        OP_LSHIFT,        OP_NONE)
	TOKENOP(TK_RSHIFT,        OP_RSHIFT,        OP_NONE)
	TOKENOP(TK_ADD,           OP_ADD,           OP_POS)
	TOKENOP(TK_SUB,           OP_SUB,           OP_NEG)
	TOKENOP(TK_MUL,           OP_MUL,           OP_DEREF)
  */
static token_op_t g_token_op[] = 
{
#define TOKENOP(tok, bop, uop) {bop, uop},
#include "tokenop.h"
};

/*
 	operators' precedence
 */
static int g_precedence[] =
{
#define OPINFO(op, prec, name, func, opcode) prec,
#include "opinfo.h"
	0
};

/*
 	operators' name
 */
static const char* g_op_name[] =
{
#define OPINFO(op, prec, name, func, opcode) name,
#include "opinfo.h"
	0
};

const char* get_op_name(int op)
{
    if ((int)op < 0 || (int)op > COUNTOF(g_op_name)) {
        return "unkown";
    }
    return g_op_name[op];
}

/**
 *  primary-expression:
 *		ID
 *		constant
 *		string-literal
 *		( expression )
 */
expr_t* parse_primary_expression()
{
    auto tok = tokc;
    expr_t* expr = nullptr;
    switch (tokc.kind)
    {
    case TK_ID:
    {
        NEW_EXPR(expr);
        expr->op = OP_ID;
        expr->token = tokc;
        expr->loc = tokc.loc;
        expr->val = tokc.val;
        token_next();
        break;
    }
    case TK_INTCONST:
    case TK_DOUBLECONST:
    {
        NEW_EXPR(expr);
        expr->op = OP_CONST;
        expr->token = tokc;
		expr->ty = T(INT + tokc.kind - TK_INTCONST);
        expr->loc = tokc.loc;
        expr->val = tokc.val;
        token_next();
        break;
    }
    case TK_STRING:
    {
        NEW_EXPR(expr);
        expr->op = OP_STR;
        expr->token = tokc;
		expr->ty = &g_string_type;
        expr->loc = tokc.loc;
        expr->val = tokc.val;
        token_next();
        break;
    }
    case TK_CHAR:
    {
        NEW_EXPR(expr);
        expr->op = OP_CHAR;
        expr->token = tokc;
		expr->ty = T(CHAR);
        expr->loc = tokc.loc;
        expr->val = tokc.val;
        token_next();
        break;
    }
    case TK_TRUE:
    case TK_FALSE:
    {
        NEW_EXPR(expr);
        expr->op = OP_BOOL;
        expr->token = tokc;
		expr->ty = T(CHAR);
        expr->loc = tokc.loc;
        expr->val.i8 = tokc.kind == TK_TRUE ? 1 : 0;
        token_next();
        break;
    }
    case TK_NULL:
    {
        NEW_EXPR(expr);
        expr->op = OP_NULL;
        expr->token = tokc;
		expr->ty = nullptr;
        expr->loc = tokc.loc;
        expr->val = tokc.val;
        token_next();
        break;
    }
    case TK_LPAREN:
    {
        NEW_EXPR(expr);
        expr->op = OP_LPAREN;
        expr->token = tokc;
        expr->loc = tokc.loc;
        expr->val = tokc.val;
        token_next();
        expr->kids[1] = (expr_t*)parse_expression();
        NEXT_ON_KIND(expr, TK_RPAREN);
        break;
    }
    case TK_RPAREN:
    case TK_NONE:
        return nullptr;
	default:
        mark_line();
        printf("parsing expression: unexpected token:\n");
        print_token(&tokc);
        return nullptr;
    }
    if (expr) {
        expr->loc = tok.loc;
    }
    dbg(print_expression(expr));
    return expr;
}

expr_t* parse_specify_expression();

/**
 *  postfix-expression:
 *		primary-expression
 *		postfix-expression [ expression ]
 *		postfix-expression ( [argument-expression-list] )
 *		postfix-expression . identifier
 *		postfix-expression -> identifier
 *		postfix-expression ++
 *		postfix-expression --
 */
expr_t* parse_postfix_expression(void)
{
    auto tok = tokc;
    expr_t* expr = nullptr;

	expr = parse_primary_expression();

	while (1)
	{
		switch (tokc.kind)
		{
		case TK_LBRACKET:	/* postfix-expression [ expression ] */
        {
            expr_t* expr2 = nullptr;
            NEW_EXPR(expr2);
            expr2->op = OP_INDEX;
            expr2->token = tokc;
            expr2->loc = tokc.loc;
            expr2->val = tokc.val;
            expr2->kids[0] = expr;
            token_next();
			expr2->kids[1] = (expr_t*)parse_expression();
			expr = expr2;
			NEXT_ON_KIND(expr, TK_RBRACKET);
			break;
        }
		case TK_LPAREN:		/* postfix-expression ( [argument-expression-list] ) */
        {
            if (expr->op != OP_ID && expr->op != OP_MEMBER)
                return expr;
            expr_t* expr2 = nullptr;
            NEW_EXPR(expr2);
            expr2->op = OP_CALL;
            expr2->token = expr->token;
            expr2->loc = expr->loc;
            expr2->kids[0] = expr;
            token_next();
            expr2->kids[1] = (expr_t*)parse_specify_expression();
            auto end = expr2->kids[1];
            while (tokc.kind == TK_COMMA) {
                token_next();
                end->next = (expr_t*)parse_specify_expression();
                end = (expr_t*)end->next;
            }
            expr = expr2;
            NEXT_ON_KIND(expr, TK_RPAREN);
			break;
        }
		case TK_DOT:		/* postfix-expression . identifier */
		case TK_POINTER:	/* postfix-expression -> identifier */
        {
            expr_t* expr2 = nullptr;
            NEW_EXPR(expr2);
			expr2->op = (tokc.kind == TK_DOT ? OP_MEMBER : OP_PTR_MEMBER);
			expr2->token = tokc;
			expr2->kids[0] = expr;
			token_next();
            EXPECT_TOKEN(expr2, TK_ID);
            expr2->kids[1] = parse_primary_expression();
            expr = expr2;
			break;
        }
		case TK_INC:	/* postfix-expression ++ */
		case TK_DEC:	/* postfix-expression -- */
        {
            expr_t* expr2 = nullptr;
            NEW_EXPR(expr2);
			expr2->op = (tokc.kind == TK_INC) ? OP_POSTINC : OP_POSTDEC;
			expr2->token = tokc;
			expr2->kids[0] = expr;
			token_next();
			expr = expr2;
			break;
        }
		default:
            if (expr) {
                expr->loc = tok.loc;
            }
			return expr;
		}
        dbg(print_expression(expr));
	}
    if (expr) {
        expr->loc = tok.loc;
    }
    return expr;
}

expr_t* parse_unary_expression(void)
{
    auto tok = tokc;
    expr_t* expr = nullptr;
    switch (tokc.kind)
    {
    case TK_INC:    // ++
    case TK_DEC:    // --
    case TK_NOT:    // !
    case TK_COMP:   // ~
    case TK_BITAND: // &
    case TK_ADD:    // +
    case TK_SUB:    // -
    case TK_MUL:    // *
    {
        NEW_EXPR(expr);
        expr->op = CUR_UNARY_OP();
        expr->token = tokc;
        expr->loc = tokc.loc;
        token_next();
        expr->kids[0] = parse_unary_expression();
        break;
    }
    default:
        expr = parse_postfix_expression();
    }
    if (expr) {
        expr->loc = tok.loc;
    }
    return expr;
}

expr_t* parse_binary_expression(int prec)
{
    auto tok = tokc;
    expr_t* expr = parse_unary_expression();
    int new_prec = 0;
    while (IS_BINARY(tokc.kind) && (new_prec = g_precedence[CUR_BINARY_OP()]) >= prec) {
        expr_t* bin = nullptr;
        NEW_EXPR(bin);
        bin->token = tokc;
        bin->loc = tokc.loc;
        bin->op = CUR_BINARY_OP();
        bin->kids[0] = expr;
        token_next();
        bin->kids[1] = parse_binary_expression(new_prec + 1);
        expr = bin;
        dbg(print_expression(expr));
    }
    if (expr) {
        expr->loc = tok.loc;
    }
    return expr;
}

expr_t* parse_cond_expression()
{
    auto tok = tokc;
    expr_t* expr = parse_binary_expression(g_precedence[OP_OR]);
    if (expr == nullptr) {
        return nullptr;
    }
	if (tokc.kind == TK_QUESTION) {
        expr_t* expr2 = nullptr;
        NEW_EXPR(expr2);
        expr2->token = tokc;
        expr2->loc = tokc.loc;
        expr2->op = OP_QUESTION;
        expr2->kids[0] = expr;
        token_next();
        expr_t* sub = nullptr;
        NEW_EXPR(sub);
        expr2->kids[1] = sub;
        auto tok2 = tokc; 
        sub->kids[0] = (expr_t*)parse_cond_expression();
        if (sub->kids[0] == nullptr) {
            do_error(&tok2.loc, "expect expression here");
            free_nodes(expr2);
            return nullptr;
        }
        sub->token = tokc;
        NEXT_ON_KIND(expr2, TK_COLON);
        tok2 = tokc;
        sub->loc = tokc.loc;
        sub->op = OP_COLON;
        sub->kids[1] = parse_cond_expression();
        if (sub->kids[1] == nullptr) {
            do_error(&tok2.loc, "expect expression here");
            free_nodes(expr2);
            return nullptr;
        }
        expr = expr2;
        dbg(print_expression(expr));
    }
    if (expr) {
        expr->loc = tok.loc;
    }
    return expr;
}

expr_t* parse_assign_expression()
{
    auto tok = tokc;
    expr_t* expr = parse_cond_expression();
	if (tokc.kind >= TK_ASSIGN && tokc.kind <= TK_MOD_ASSIGN) {
        expr_t* expr2 = nullptr;
        NEW_EXPR(expr2);
        expr2->token = tokc;
        expr2->loc = tokc.loc;
        expr2->op = CUR_BINARY_OP();
        expr2->kids[0] = expr;
        token_next();
        expr2->kids[1] = parse_assign_expression();        
        expr = expr2;
        dbg(print_expression(expr));
    }
    if (expr) {
        expr->loc = tok.loc;
    }
    return expr;
}

expr_t* parse_specify_expression()
{
    if (get_ast_error_count() > 1000) {
        return nullptr;
    }
    auto tok = tokc;
	expr_t* expr = parse_assign_expression();
	if (tokc.kind == TK_COLON) {
        expr_t* specify = nullptr;
        NEW_EXPR(specify);

		specify->op = OP_SPECIFY;
		specify->kids[0] = expr;
		token_next();
		specify->kids[1] = parse_assign_expression();

        dbg(print_expression(expr));
		expr = specify;
    }
    dbg(print_expression(expr));
    return expr;
}

ast_node_t* parse_expression()
{
    if (get_ast_error_count() > 1000) {
        return nullptr;
    }
    auto tok = tokc;
	expr_t* expr = parse_specify_expression();
	while(tokc.kind == TK_COMMA)
	{
        expr_t* comma = nullptr;
        NEW_EXPR(comma);

		comma->op = OP_COMMA;
		comma->kids[0] = expr;
		token_next();
		comma->kids[1] = parse_specify_expression();

        dbg(print_expression(expr));
		expr = comma;
	}
    if (expr) {
        expr->loc = tok.loc;
    }
    dbg(print_expression(expr));
    return expr;
}

/**
 * Parse constant expression which is actually a conditional expression
 */ 
/*
	constant-expression:	conditional-expression 
 */
expr_t* parse_constant_expression(void)
{
	return parse_cond_expression();
}

static void do_print_expression(expr_t* expr, int tab)
{
    if (expr == NULL) {
        return;
    }
    // fprint_location(stdout, &expr->loc);
    printf("    ");
    for (int i = 0; i < tab; ++i)
    {
        if (i + 1 < tab)
            printf("|   ");
        else
            printf("|---");
    }
    if (expr == nullptr) {
        printf("\n");
        return;
    }
    if (expr->op == OP_STR) {
        printf("\"%s\"\n", ref_to_string(&expr->token.ss));
    } else {
        if (expr->op == OP_CALL) {
            printf("[call]\n");
        } else if (expr->op == OP_SPECIFY) {
            printf("[specify]\n");
        } else {
            printf("%s\n", ref_to_string(&expr->token.ss));
        }
        if (expr->op != OP_CONST && expr->op != OP_ID) {
            do_print_expression(expr->kids[0], tab + 1);
            do_print_expression(expr->kids[1], tab + 1);
            if (expr->kids[1]) {
                expr = (expr_t*)expr->kids[1]->next;
                while (expr) {
                    do_print_expression(expr, tab + 1);
                    expr = (expr_t*)expr->next;
                }
            }
        }
    }
    // if (expr->op == OP_CONST) {
    //     printf("%s", g_op_name[expr->op]);
    //     int categ = expr->ty->categ;

    //     if (categ == INT || categ == LONG || categ == LONGLONG)
    //     {
    //         fprintf(stdout, "%d", expr->val.i[0]);
    //     }
    //     else if (categ == UINT || categ == ULONG || categ == ULONGLONG || categ == POINTER)
    //     {
    //         fprintf(stdout, "%u", expr->val.i[0]);
    //     }
    //     else if (categ == FLOAT)
    //     {
    //         fprintf(stdout, "%g", expr->val.f);
    //     }
    //     else
    //     {
    //         fprintf(stdout, "%g", expr->val.d);
    //     }
    //     fprintf(stdout, "\n");
    // } else {
    //     printf("%s\n", g_op_name[expr->op]);
    //     do_print_expression(expr->kids[0], tab + 1);
    //     do_print_expression(expr->kids[1], tab + 1);
    // }
}

void print_expression(ast_node_t* node)
{
    do_print_expression((expr_t*)node, 0);
}
void print_expression_s(ast_node_t* node)
{
    expr_t* expr = (expr_t*)node;
    if (expr == nullptr) {
        return;
    }
    switch (expr->op) {
    case OP_CONST: {
        printf("%s", ref_to_string(&expr->token.ss));
        break;
    } case OP_LPAREN: {
        printf("(");
        print_expression_s(expr->kids[1]);
        printf(")");
        break;
    } case OP_ID: {
        print_expression_s(expr->kids[0]);
        printf("%s", ref_to_string(&expr->token.ss));
        print_expression_s(expr->kids[1]);
        break;
    } case OP_STR: {
        printf("\"%s\"", ref_to_string(&expr->token.ss));
        break;
    } case OP_CALL: {
        print_expression_s(expr->kids[0]);
        printf("(");
        auto iter = (expr_t*)expr->kids[1];
        while (iter) {
            if (iter != expr->kids[1])
                printf(", ");
            print_expression_s(iter);
            iter = (expr_t*)iter->next;
        }
        printf(")");
        break;
    } case OP_SPECIFY: {
        print_expression_s(expr->kids[0]);
        printf(": ");
        print_expression_s(expr->kids[1]);
        break;
    } case OP_PREINC: {
        printf("++");
        print_expression_s(expr->kids[0]);
        break;
    } case OP_POSTINC: {
        print_expression_s(expr->kids[0]);
        printf("++");
        break;
    } case OP_PREDEC: {
        printf("--");
        print_expression_s(expr->kids[0]);
        break;
    } case OP_POSTDEC: {
        print_expression_s(expr->kids[0]);
        printf("--");
        break;
    } case OP_QUESTION: {
        print_expression_s(expr->kids[0]);
        printf(" ? ");
        print_expression_s(expr->kids[1]->kids[0]);
        printf(" : ");
        print_expression_s(expr->kids[1]->kids[1]);
        break;
    } case OP_MEMBER: {
        print_expression_s(expr->kids[0]);
        printf(".");
        print_expression_s(expr->kids[1]);
        break;
    } default: {
        print_expression_s(expr->kids[0]);
        printf(" %s ", ref_to_string(&expr->token.ss));
        print_expression_s(expr->kids[1]);
        break;
    }
    }
}

void free_expression(ast_node_t* node)
{
    if (node->kind != NK_Expression) {
        fprintf(stderr, "delete wrong ast node with type:%d\n", node->kind);
        return;
    }
    delete (expr_t*)node;
}
